//
// Created by gj on 16-2-2.
//

#ifndef ALLUTILS_CLOG_H
#define ALLUTILS_CLOG_H

#include "../base/CClassInformation.h"

#include <android/log.h>

class CLogConfig {
public:
    // log 等级
    enum ELogPriority {
        eLogVerbose, //
        eLogInfo, //
        eLogDebug, //
        eLogWarn, //
        eLogError,
    };
    // 日志总开关
    static bool LOG_ON;
    // 日志默认是否打开
    static bool LOG_ON_DEFAULT;
    // 日志默认标志
    static string LOG_TAG_DEFAULT;

    friend class CLogPrinter;

    friend class CLogSettings;

    void toDefault() {
        logOn = LOG_ON_DEFAULT;
        tag = LOG_TAG_DEFAULT;
    }

private:
    bool logOn;
    string tag;

    CLogConfig() //
            : logOn(LOG_ON_DEFAULT), //
              tag(LOG_TAG_DEFAULT) {
    }

    explicit CLogConfig(const char *tag) //
            : logOn(LOG_ON_DEFAULT), //
              tag(tag) {
    }

    explicit CLogConfig(const string &tag) //
            : logOn(LOG_ON_DEFAULT), //
              tag(tag) {
    }

    explicit CLogConfig(bool logOn, const char *tag) //
            : logOn(logOn), tag(tag) {
    }

    explicit CLogConfig(bool logOn, bool logShowThread, const string &tag) //
            : logOn(logOn), tag(tag) {
    }

    explicit CLogConfig(const CLogConfig &logConfig) //
            : logOn(logConfig.logOn), //
              tag(logConfig.tag) {
    }
};

class CLogStream {
public:
    virtual void print(CLogConfig::ELogPriority priority, const char *tag, const char *message,
                       va_list &ap) const = 0;
};

class CLogAndroidStream : public CLogStream {
public:
    virtual void print(CLogConfig::ELogPriority priority, const char *tag, const char *message,
                       va_list &ap) const;
};

class CLogPrinter {

#define IS_SHOW_LOG if(!mLogConfig.logOn) return;

#define LOG_FUNCTION_DECLARATION(_func_name_) \
    void _func_name_(const char *message, ...) const;

#define LOG_FUNCTION_IMPLEMENTATIONS(_func_name_, _log_priority_) \
    void CLogPrinter::_func_name_(const char *message, ...) const { \
        IS_SHOW_LOG \
        va_list ap; \
        va_start(ap, message); \
        _log(_log_priority_, message, ap); \
        va_end(ap); \
    }

public:
    CLogPrinter() //
            : m_pLogStream(&LOG_DEF_STREAM) {
    }

    explicit CLogPrinter(const char *tag) //
            : mLogConfig(tag), m_pLogStream(&LOG_DEF_STREAM) {
    }

    explicit CLogPrinter(const string &tag) //
            : mLogConfig(tag), m_pLogStream(&LOG_DEF_STREAM) {
    }

    explicit CLogPrinter(const CClassInformation &_class) //
            : mLogConfig(_class.getClassName()), m_pLogStream(&LOG_DEF_STREAM) {
    }

    virtual ~CLogPrinter() {
        if (m_pLogStream != &LOG_DEF_STREAM && m_pLogStream != NULL) {
            delete m_pLogStream;
        }
    }

    CLogConfig &getLogConfig() {
        return mLogConfig;
    }

    LOG_FUNCTION_DECLARATION(v)

    LOG_FUNCTION_DECLARATION(i)

    LOG_FUNCTION_DECLARATION(d)

    LOG_FUNCTION_DECLARATION(w)

    LOG_FUNCTION_DECLARATION(e)

protected:
    static CLogAndroidStream LOG_DEF_STREAM;

    CLogConfig mLogConfig;
    const CLogStream *m_pLogStream;

    void _log(CLogConfig::ELogPriority priority, const char *message, va_list &ap) const {
        m_pLogStream->print(priority, mLogConfig.tag.c_str(), message, ap);
    }
};

class CLogSettings {
public:
    CLogSettings(CLogPrinter &mLogPrinter) //
            : mLogPrinter(mLogPrinter), //
              mLogConfig(mLogPrinter.getLogConfig()), //
              mResetConfig(mLogPrinter.getLogConfig()) {
        //
    }

    CLogPrinter &getLogPrinter() {
        return mLogPrinter;
    }

    CLogSettings &reset() {
        copyConfigAToB(mResetConfig, mLogConfig);
        return *this;
    }

    CLogSettings &isShowLog(bool isShow) {
        mLogConfig.logOn = isShow;
        return *this;
    }

    CLogSettings &setTag(const string &tag) {
        mLogConfig.tag = tag;
    }

    CLogSettings &toDefaultSettings() {
        mLogConfig.toDefault();
        return *this;
    }

private:
    CLogPrinter &mLogPrinter;
    CLogConfig &mLogConfig;
    CLogConfig mResetConfig;

    void copyConfigAToB(CLogConfig &a, CLogConfig &b) {
        b.logOn = a.logOn;
        b.tag = a.tag;
    }
};

#define LOG_EXT_FUNC(message) \
    "|Function --> %s\n|Line --> %d\n|"message, __FUNCTION__, __LINE__
#define LOG_EXT_FILE_FUNC(message) \
    "|File --> %s\n|Function --> %s\n|Line --> %d\n|"message, __FILE__, __FUNCTION__, __LINE__
#define LOG_ON_CONFIG if(CLogConfig::LOG_ON)
#define LOG_SEPARATOR "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"

#define LOG_CREATE_NAME(name) CLogPrinter name;
#define LOG_CREATE_NAME_FUNC(name, tag) CLogPrinter name(tag);
#define LOG_E_NAME(name, message, ...) LOG_ON_CONFIG name.e(message, ##__VA_ARGS__);
#define LOG_E_NAME_FUNC(name, message, ...) LOG_ON_CONFIG name.e(LOG_EXT_FUNC(message), ##__VA_ARGS__);
#define LOG_E_NAME_FILE_FUNC(name, message, ...) LOG_ON_CONFIG name.e(LOG_EXT_FILE_FUNC(message), ##__VA_ARGS__);


#define LOG_DEF_NAME logPrinter
#define LOG_INIT_NAME(tag) LOG_DEF_NAME(tag),

#define LOG_CREATE LOG_CREATE_NAME(LOG_DEF_NAME)
#define LOG_CREATE_FUNC(tag) LOG_CREATE_NAME_FUNC(LOG_DEF_NAME, tag)
#define LOG_E(message, ...) LOG_E_NAME(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_E_FUNC(message, ...) LOG_E_NAME_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_E_FILE_FUNC(message, ...) LOG_E_NAME_FILE_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)

#ifdef FLAG_LOG_DEBUG

extern CLogPrinter debugLogger;

#define LOG_INIT_NAME_DEBUG(tag) LOG_DEF_NAME(tag),
#define LOG_CREATE_NAME_DEBUG(name) CLogPrinter name;
#define LOG_CREATE_NAME_FUNC_DEBUG(name, tag) CLogPrinter name(tag);

#define LOG_V_NAME(name, message, ...) LOG_ON_CONFIG name.v(message, ##__VA_ARGS__);
#define LOG_I_NAME(name, message, ...) LOG_ON_CONFIG name.i(message, ##__VA_ARGS__);
#define LOG_D_NAME(name, message, ...) LOG_ON_CONFIG name.d(message, ##__VA_ARGS__);
#define LOG_W_NAME(name, message, ...) LOG_ON_CONFIG name.w(message, ##__VA_ARGS__);

#define LOG_V_NAME_FUNC(name, message, ...) LOG_ON_CONFIG name.v(LOG_EXT_FUNC(message), ##__VA_ARGS__);
#define LOG_I_NAME_FUNC(name, message, ...) LOG_ON_CONFIG name.i(LOG_EXT_FUNC(message), ##__VA_ARGS__);
#define LOG_D_NAME_FUNC(name, message, ...) LOG_ON_CONFIG name.d(LOG_EXT_FUNC(message), ##__VA_ARGS__);
#define LOG_W_NAME_FUNC(name, message, ...) LOG_ON_CONFIG name.w(LOG_EXT_FUNC(message), ##__VA_ARGS__);

#define LOG_V_NAME_FILE_FUNC(name, message, ...) LOG_ON_CONFIG name.v(LOG_EXT_FILE_FUNC(message), ##__VA_ARGS__);
#define LOG_I_NAME_FILE_FUNC(name, message, ...) LOG_ON_CONFIG name.i(LOG_EXT_FILE_FUNC(message), ##__VA_ARGS__);
#define LOG_D_NAME_FILE_FUNC(name, message, ...) LOG_ON_CONFIG name.d(LOG_EXT_FILE_FUNC(message), ##__VA_ARGS__);
#define LOG_W_NAME_FILE_FUNC(name, message, ...) LOG_ON_CONFIG name.w(LOG_EXT_FILE_FUNC(message), ##__VA_ARGS__);

#else

#define LOG_INIT_NAME_DEBUG(tag)
#define LOG_CREATE_NAME_DEBUG(name)
#define LOG_CREATE_NAME_FUNC_DEBUG(name, tag)

#define LOG_V_NAME(name, message, ...)
#define LOG_I_NAME(name, message, ...)
#define LOG_D_NAME(name, message, ...)
#define LOG_W_NAME(name, message, ...)

#define LOG_V_NAME_FUNC(name, message, ...)
#define LOG_I_NAME_FUNC(name, message, ...)
#define LOG_D_NAME_FUNC(name, message, ...)
#define LOG_W_NAME_FUNC(name, message, ...)

#define LOG_V_NAME_FILE_FUNC(name, message, ...)
#define LOG_I_NAME_FILE_FUNC(name, message, ...)
#define LOG_D_NAME_FILE_FUNC(name, message, ...)
#define LOG_W_NAME_FILE_FUNC(name, message, ...)

#endif

#define LOG_CREATE_DEBUG LOG_CREATE_NAME_DEBUG(LOG_DEF_NAME)
#define LOG_CREATE_FUNC_DEBUG(tag)  LOG_CREATE_NAME_FUNC_DEBUG(LOG_DEF_NAME, tag)

#define LOG_V(message, ...) LOG_V_NAME(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_I(message, ...) LOG_I_NAME(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_D(message, ...) LOG_D_NAME(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_W(message, ...) LOG_W_NAME(LOG_DEF_NAME, message, ##__VA_ARGS__)

#define LOG_V_FUNC(message, ...) LOG_V_NAME_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_I_FUNC(message, ...) LOG_I_NAME_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_D_FUNC(message, ...) LOG_D_NAME_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_W_FUNC(message, ...) LOG_W_NAME_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)

#define LOG_V_FILE_FUNC(message, ...) LOG_V_NAME_FILE_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_I_FILE_FUNC(message, ...) LOG_I_NAME_FILE_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_D_FILE_FUNC(message, ...) LOG_D_NAME_FILE_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)
#define LOG_W_FILE_FUNC(message, ...) LOG_W_NAME_FILE_FUNC(LOG_DEF_NAME, message, ##__VA_ARGS__)


#undef FLAG_LOG_DEBUG
// --------------------------------------------- unit test --------------------------------------------------
#ifdef FLAG_UNIT_TESTING

void log_unit_testing();

#endif

#endif //ALLUTILS_CLOG_H


